﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace CH.Study.ConsoleApp.ExpressionTree
{
    public class QueryExpressionAnalyse : QueryExpressionVisitor
    {
        private Expression expression;
        private QueryExpressionType nodeType;
        private int index = 0;
        public Expression Analyse(Expression expression) 
        {
            this.expression = expression;
            this.nodeType=(QueryExpressionType)expression.NodeType;
            if (this.nodeType != QueryExpressionType.Select && this.nodeType != QueryExpressionType.Insert && this.nodeType != QueryExpressionType.Delete && this.nodeType != QueryExpressionType.Update)
            {
                this.expression = new SelectExpression(expression);
                this.nodeType = (QueryExpressionType)this.expression.NodeType;
            }
            this.Visit(expression);
            if (this.nodeType == QueryExpressionType.Select)
            {
                SelectExpression se = this.expression as SelectExpression;
                if (se != null)
                {
                    if (se.ColumnExp == null)
                    {
                        se.ColumnExp = new ColumnExpression(Expression.Constant("*"));
                    }

                    if (se.SkipExp == null)
                    {
                        se.SkipExp = new SkipExpression(null);
                    }

                    if (se.TakeExp == null)
                    {
                        se.TakeExp = new TakeExpression(null);
                    }
                    if (se.GroupExp == null)
                    {
                        se.GroupExp = new GroupExpression(null);
                    }

                    if (se.InnerJoinExp == null)
                    {
                        se.InnerJoinExp = new InnerJoinExpression(null);
                    }

                    if (se.InnerJoinExp.Expression != null) 
                    {
                        se.IsInnerJoin = true;
                    }


                    if (se.TakeExp.Expression != null && se.SkipExp.Expression != null)
                    {
                        int take = (int)(((ConstantExpression)se.TakeExp.Expression).Value);
                        int skip = (int)(((ConstantExpression)se.SkipExp.Expression).Value);
                        if (skip == 0 || take == 0)
                        {
                            se.PageIndex = 1;
                        }
                        else
                        {
                            se.PageIndex = (skip / take) + 1;
                        }
                        se.PageSize = take;
                        se.IsPage = true;
                    }

                }
            }
            return this.expression;
        }
        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            SelectExpression se = this.expression as SelectExpression;
            if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Take")
            {
                se.TakeExp = new TakeExpression(m.Arguments[1]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Skip")
            {
                se.SkipExp = new SkipExpression(m.Arguments[1]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where")
            {
                se.WhereExp = new WhereExpression(m.Arguments[1]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Select")
            {
                se.ColumnExp = new ColumnExpression(m.Arguments[1]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && (m.Method.Name == "Sum" || m.Method.Name == "Count" || m.Method.Name == "Average" || m.Method.Name == "Max" || m.Method.Name == "Min"))
            {
                se.ColumnExp = new ColumnExpression(m);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && (m.Method.Name == "OrderBy" || m.Method.Name == "ThenBy"))
            {
                se.OrderExp.AddOrder(Expression.Call(typeof(ExpressionMethodExtend).GetMethod("Order"), m.Arguments[1], Expression.Parameter(typeof(OrderType), OrderType.ASC.ToString())));
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && (m.Method.Name == "OrderByDescending" || m.Method.Name == "ThenByDescending"))
            {
                se.OrderExp.AddOrder(Expression.Call(typeof(ExpressionMethodExtend).GetMethod("Order"), m.Arguments[1], Expression.Parameter(typeof(OrderType), OrderType.DESC.ToString())));
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "GroupBy")
            {
                se.GroupExp = new GroupExpression(m.Arguments[1]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Join")
            {
                se.ColumnExp = new ColumnExpression(m.Arguments[4]);
                se.InnerJoinExp = new InnerJoinExpression(m, new TableExpression(m.Arguments[1], "t" + index++), m.Arguments[0], m.Arguments[1], m.Arguments[2], m.Arguments[3], m.Arguments[4]);
            }
            this.Visit(m.Arguments[0]);
            return m;
        }
        protected override Expression VisitConstant(ConstantExpression c)
        {
            SelectExpression se = this.expression as SelectExpression;
            IQueryable q = c.Value as IQueryable;
            if (q != null)
            {
                se.FromExp.Table = new TableExpression(c, "t" + index);
                index += 1;
            }
            return c;
        }
    }
}
